package com.hursing.tractor.model;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import com.hursing.tractor.controller.KittyController;
import com.hursing.tractor.controller.DealController;
import com.hursing.tractor.controller.PlayController;

public abstract class Player {
	public static final int POSITION_UNKNOWN = -1;
	public static final int POSITION_BOTTOM = 0;
	public static final int POSITION_RIGHT = 1;
	public static final int POSITION_TOP = 2;
	public static final int POSITION_LEFT = 3;
	public static final int PLAYER_COUNT = 4;
	
	public static final int SIDE_TOP_BOTTOM = 5;
	public static final int SIDE_LEFT_RIGHT = 6;
	
	private final int mPosition;
	private final int mSide;
	private final boolean[] mFlipAbility = new boolean[Card.COUNT_OF_SUIT_TYPE];
	protected final ArrayList<LinkedList<Card>> mHandyCards = new ArrayList<LinkedList<Card>>(Card.COUNT_OF_SUIT_TYPE);
	protected LinkedList<Card> mPlayedCards = new LinkedList<Card>();
	
	private KittyController mKittyController;
	private PlayController mPlayController;
	
	public Player(int position) {
		mPosition = position;
		int count = Card.COUNT_OF_SUIT_TYPE;
		while (count-- > 0) {
			mHandyCards.add(new LinkedList<Card>());
		}
		if (mPosition == POSITION_LEFT || mPosition == POSITION_RIGHT) {
			mSide = SIDE_LEFT_RIGHT;
		} else {
			mSide = SIDE_TOP_BOTTOM;
		}
	}
	
	protected abstract void judgeWhetherFlip(DealController dealController, Card newCard);
	
	public void drawOneCard(DealController dealController) {
		Card card = dealController.dealOneCard();
		addOneCardToHand(card);
		judgeWhetherFlip(dealController, card);
	}
	
	public void drawKitty(KittyController kittyController) {
		List<Card> kitty = kittyController.dealKitty();
		for (int i = 0; i < kitty.size(); i++) {
			Card card = kitty.get(i);
			addOneCardToHand(card);
		}
	}
	
	public LinkedList<Card> allHandyCards() {
		LinkedList<Card> cards = new LinkedList<Card>();
		for (int i = Card.SUIT_TRUMP; i >= 0; i--) {
			cards.addAll(mHandyCards.get(i));
		}
		return cards;
	}
	
	protected void addOneCardToHand(Card card) {
		LinkedList<Card> cards = mHandyCards.get(card.suitDistinguishTrump());
		cards.add(card);
		Card.sortForHeight(cards);
	}

	protected void removeCardsFromHand(LinkedList<Card> cards) {
		for (int i = 0; i < cards.size(); i++) {
			Card card = cards.get(i);
			mHandyCards.get(card.suitDistinguishTrump()).remove(card);
		}
	}
	
	public int handyCardCount() {
		int count = 0;
		for (int i = mHandyCards.size() - 1; i >= 0; i--) {
			count += mHandyCards.get(i).size();
		}
		return count;
	}
	
	public void buryKitty(KittyController kittyController) {
		mKittyController = kittyController;
	}
	
	public void doBuryCards(LinkedList<Card> cards) {
		removeCardsFromHand(cards);
		mKittyController.collectKitty(cards);
		mKittyController = null;
	}
	
	public void playCard(PlayController playController) {
		mPlayController = playController;
	}
	
	public void doPlayCard(LinkedList<Card> cards) {
		mPlayController.collectPlayedCard(this, cards);
		removeCardsFromHand(cards);
	}
	
	public LinkedList<Card> canThrow(CardPattern cp) {
		int suit = cp.cards().getFirst().suitDistinguishTrump();
		CardPattern my = new CardPattern(getCardsInSuit(suit), false);
		return my.rejectThrow(cp);
	}
	
	public boolean flipAbility(int suit) {
		return mFlipAbility[suit];
	}
	
	public int position() {
		return mPosition;
	}
	
	public int side() {
		return mSide;
	}
	
	public void getReadyForNewGame() {
		clearFlipAbility();
		mPlayedCards.clear();
	}
	
	protected void setFlipAbilityOfSuit(int suit, boolean able) {
		mFlipAbility[suit] = able;
	}
	
	protected void clearFlipAbility() {
		for (int i = 0; i < mFlipAbility.length; i++) {
			setFlipAbilityOfSuit(i, false);
		}
	}
	
	public LinkedList<Card> getCardsInSuit(int suit) {
		return mHandyCards.get(suit);
	}
	
	public boolean checkLegal(LinkedList<Card> cards) {
		return Strategy.instance().checkLegalBeforePlay(this, cards, mPlayController);
	}
	
	/**
	 * 非亮主可否反牌
	 * @param card
	 * @return
	 */
	private boolean canChangeFlipAbilityWhenNotFlipper(Card card) {
		int count = Collections.frequency(mHandyCards.get(Card.SUIT_TRUMP), card);
		if (card.suit() == Card.SUIT_JOKER) {
			if (count >= Strategy.MINIMUN_COUNT_OF_FLIPPING_JOKER) {
				if (count > GameData.instance().flippedCards().size()
						|| (count == GameData.instance().flippedCards().size() 
								&& card.isLargerThanOther(GameData.instance().flippedCards().getFirst())
							)
					) {
					setFlipAbilityOfSuit(Card.SUIT_JOKER, true);
					return true;
				}
			}
		} else {
			if (count > GameData.instance().flippedCards().size()) {
				setFlipAbilityOfSuit(card.suit(), true);
				return true;
			}
		}
		return false;
	}
	
	private void fullyCheckFlipAbility() {
		clearFlipAbility();
		if (GameData.instance().flipperPosition() == position()) {
			Card card = GameData.instance().flippedCards().get(0);
			int count = Collections.frequency(mHandyCards.get(Card.SUIT_TRUMP), card);
			if (count > GameData.instance().flippedCards().size()) {
				setFlipAbilityOfSuit(card.suit(), true);
			}
		} else {
			canChangeFlipAbilityWhenNotFlipper(Card.largeJoker);
			canChangeFlipAbilityWhenNotFlipper(Card.smallJoker);
			for (int i = 0; i < Card.SUIT_COUNT_WITHOUT_JOKER; i++) {
				Card card = Card.getCard(i, GameData.instance().getCurrentMaster());
				canChangeFlipAbilityWhenNotFlipper(card);
			}
		}
	}
	
	protected boolean canChangeFlipAbility(Card newCard) {
		if (newCard.suit() != Card.SUIT_JOKER && newCard.number() != GameData.instance().getCurrentMaster()) {
			return false;
		}
		
		if (GameData.instance().flipperPosition() == position()) {
			if (GameData.instance().flippedCards().get(0).equals(newCard)) {
				setFlipAbilityOfSuit(newCard.suit(), true);
				return true;
			}
			return false;
		} else {
			return canChangeFlipAbilityWhenNotFlipper(newCard);
		}
	}
	
	public void flip(int suit) {
		if (mFlipAbility[suit] == false) {
			return;
		}
				
		Card card = null;
		int count = 0;
		List<Card> trumpCards = mHandyCards.get(Card.SUIT_TRUMP);
		if (suit == Card.SUIT_JOKER) {
			card = Card.largeJoker;
			count = Collections.frequency(trumpCards, card);
			if (count < GameData.instance().flippedCards().size() || count < Strategy.MINIMUN_COUNT_OF_FLIPPING_JOKER) {
				card = Card.smallJoker;
				count = Collections.frequency(trumpCards, card);
			}
		} else {
			card = Card.getCard(suit, GameData.instance().getCurrentMaster());
			count = Collections.frequency(trumpCards, card);
		}
		
		ArrayList<Card> flippedCards = new ArrayList<Card>(count);
		while(count-- > 0) {
			flippedCards.add(card);
		}
		GameData.instance().flip(this, flippedCards);
		
		Card.calculateCardOrder();
		for (int i = 0; i < PLAYER_COUNT; i++) {
			GameData.instance().player(i).sortTrumpCards().fullyCheckFlipAbility();
		}
	}
	
	protected Player sortTrumpCards() {
		LinkedList<Card> trumpCards = mHandyCards.get(Card.SUIT_TRUMP);
		for (int i = trumpCards.size() - 1; i >= 0; i--) {
			Card card = trumpCards.get(i);
			if (card.isTrump()) {
				break;
			} else {
				mHandyCards.get(card.suit()).addFirst(card);
				trumpCards.remove(card);
			}
		}
		Card.sortForHeight(trumpCards);
		int suit = GameData.instance().getFlippedSuit();
		if (suit != Card.SUIT_JOKER) {
			LinkedList<Card> newTrumpCards = mHandyCards.get(suit);
			trumpCards.addAll(newTrumpCards);
			newTrumpCards.clear();
		}
		return this;
	}
	
}
